﻿using System.Collections.Generic;
using Deferred.Library.Configuration;
using Deferred.Library.Scene;
using Deferred.Library.Scene.Visual;
using Microsoft.Xna.Framework.Graphics;

namespace Deferred.Library.Deferred
{
    class LightRenderEngine : GraphicalComponent
    {
        private DeferredBase deferredBase;
        private Effect drawLightDebugEffect;
        private AmbientLightRenderEngine ambientRenderer;
        private DirectionalLightRenderEngine directionalRenderer;
        private PointLightRenderEngine pointRenderer;
        private SpotLightRenderEngine spotRenderer;

        private List<AmbientLight> defaultAmbientLights;

        private IOptions options;

        public LightRenderEngine(DeferredBase deferredBase)
            : base(deferredBase.DeviceService)
        {
            this.deferredBase = deferredBase;

            ambientRenderer = new AmbientLightRenderEngine(deferredBase);
            directionalRenderer = new DirectionalLightRenderEngine(deferredBase);
            pointRenderer = new PointLightRenderEngine(deferredBase);
            spotRenderer = new SpotLightRenderEngine(deferredBase);

            defaultAmbientLights = new List<AmbientLight>();
            defaultAmbientLights.Add(new AmbientLight(Color.White, 1));
        }

        public void Initialize()
        {
            drawLightDebugEffect = deferredBase.Content.InternalContent.Load<Effect>("DrawLightDebug");

            ambientRenderer.Initialize();
            directionalRenderer.Initialize();
            pointRenderer.Initialize();
            spotRenderer.Initialize();

            options = (IOptions)deferredBase.Services.GetService(typeof(IOptions));

            if (options != null) options.AddOption("Render.RenderLights", true);
        }

        public void DrawLights(IList<AmbientLight> ambientLights, IList<DirectionalLight> directionalLights, IList<PointLight> pointLights, IList<SpotLight> spotLights, CameraVisual camera, SceneGraphVisual scene)
        {
            GraphicsDevice.Clear(Color.TransparentBlack);
            GraphicsDevice.RenderState.AlphaBlendEnable = true;
            GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
            GraphicsDevice.RenderState.SourceBlend = Blend.One;
            GraphicsDevice.RenderState.DestinationBlend = Blend.One;
            GraphicsDevice.RenderState.SeparateAlphaBlendEnabled = false;
            GraphicsDevice.RenderState.DepthBufferEnable = false;

            if (camera != null)
            {
                if (null == options || options["Render.RenderLights"])
                {
                    ambientRenderer.drawAmbientLights(ambientLights);
                    directionalRenderer.drawDirectionalLights(directionalLights, camera);
                    pointRenderer.drawPointLights(pointLights, camera);
                    spotRenderer.drawSpotLights(spotLights, camera, scene);
                }
                else
                {
                    ambientRenderer.drawAmbientLights(defaultAmbientLights);
                }
            }

            GraphicsDevice.SetRenderTarget(0, null);
        }

        public void DrawLightDebug(IList<PointLight> pointLights, IList<SpotLight> spotLights)
        {
            if (options != null && options["Render.LightDebug"] && !options["Render.DebugMode"])
            {
                GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;
                GraphicsDevice.RenderState.CullMode = CullMode.None;
                GraphicsDevice.RenderState.AlphaBlendEnable = true;
                GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
                GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
                drawLightDebugEffect.Begin();
                pointRenderer.DrawPointLightsDebug(pointLights);
                spotRenderer.DrawSpotLightsDebug(spotLights);
                drawLightDebugEffect.End();
                GraphicsDevice.RenderState.FillMode = FillMode.Solid;
                GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
                GraphicsDevice.RenderState.DepthBufferFunction = CompareFunction.LessEqual;
            }
        }
    }
}
